/******************************************************************************* * Signavio Core Components * Copyright (C) 2012 Signavio GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package org.oryxeditor.server.diagram.generic; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.log4j.Logger; import org.json.JSONException; import org.json.JSONObject; import org.oryxeditor.server.diagram.Point; import org.oryxeditor.server.diagram.StencilSetReference; /** * Represents a diagram having a certain stencilset, stencilset extensions and * shapes * * @author Philipp Maschke, Robert Gurol * * @param <S> the actual type of shape to be used (must inherit from {@link GenericShape}); calls to {@link #getChildShapesReadOnly()}, ... will return this type * @param <D> the actual type of diagram to be used (must inherit from {@link GenericDiagram}); {@link #getDiagram()} will return this type */ public abstract class GenericDiagram <S extends GenericShape<S,D>, D extends GenericDiagram<S,D>> extends GenericShapeImpl<S,D>{ //TODO certain Shape operations don't make sense, such as setDiagram... private static final Logger LOGGER = Logger.getLogger(GenericDiagram.class); private StencilSetReference stencilsetRef; private List<S> shapes; private List<String> ssextensions = new ArrayList<String>(); /** * Normal shape constructor with additional stencilsetRef * * @param resourceId * resourceId of the diagram shape * @param stencilId * stencil usually Diagram * @param stencilsetRef * StencilSet with uri and namespace */ public GenericDiagram(String resourceId, String stencilId, StencilSetReference stencilsetRef) { this(resourceId, stencilId); this.stencilsetRef = stencilsetRef; } /** * @param resourceId */ public GenericDiagram(String resourceId) { this(resourceId, null); } /** * * @param resourceId * @param stencilId */ public GenericDiagram(String resourceId, String stencilId) { super(resourceId, stencilId); this.shapes = new ArrayList<S>(); this.stencilsetRef = null; } /** * Gives the stencilset reference of a diagram * * @return the stencilsetRef */ public StencilSetReference getStencilsetRef() { return stencilsetRef; } /** * Set a new StencilSetReference * * @param stencilsetRef * the stencilsetRef to set */ public void setStencilsetRef(StencilSetReference stencilsetRef) { this.stencilsetRef = stencilsetRef; } /** * Gives a list of all namespaces of stencilset extensions used for this * diagram * * @return the ssextensions */ public List<String> getSsextensions() { return Collections.unmodifiableList(ssextensions); } /** * Set the list of all namespaces of stencilset extensions used for this * diagram * * @param ssextensions * the ssextensions to set */ public void setSsextensions(List<String> ssextensions) { this.ssextensions.clear(); if (ssextensions != null) this.ssextensions.addAll(ssextensions); } /** * Adds an additional extension namespace * * @param ssExt * the ssextension namespace to set */ public boolean addSsextension(String ssExt) { return this.ssextensions.add(ssExt); } /** * Returns all shapes of this diagram (not just direct child shapes, excluding the diagram itself) as an unmodifiable(!!!) list * * @return all shapes of this diagram as an unmodifiable list * @see #addToAllShapes(GenericShape) * @see #removeFromAllShapes(GenericShape) * @see #containsShape(GenericShape) */ public List<S> getAllShapesReadOnly() { return Collections.unmodifiableList(shapes); } /** * Returns all shapes of this diagram (not just direct child shapes, including the diagram itself) as an unmodifiable(!!!) list * * @return all shapes of this diagram and the diagram itself as an unmodifiable list * @see #addToAllShapes(GenericShape) * @see #removeFromAllShapes(GenericShape) * @see #containsShape(GenericShape) */ public List<S> getAllShapesIncludingDiagramReadOnly(){ List<S> allShapes = new ArrayList<S>(shapes); allShapes.add((S) this); return Collections.unmodifiableList(allShapes); } /** * Updates the diagram's cache of all shapes by adding a new shape * * @see #getAllShapesReadOnly() * @param newShape shape that was added to the diagram or a descendant shape */ protected void addToAllShapes(S newShape){ shapes.add(newShape); } /** * Updates the diagram's cache of all shapes by removing an existing shape * * @see #getAllShapesReadOnly() * @param removedShape shape that was removed from the diagram or a descendant shape * @return true if cached list contained the shape */ protected boolean removeFromAllShapes(S removedShape){ return shapes.remove(removedShape); } /** * Checks whether the given shape is contained in this diagram, by testing if it is contained in the <b>cached</b> list of all child shapes. * * @see #getAllShapesReadOnly() * @param shape * @return whether the shape is contained in this diagram */ public boolean containsShape(S shape){ return shapes.contains(shape); } /** * Brings the cached list of all shapes in this diagram into a new order. * <p/> * Basis for the new ordering is the given list of shape ids. This list is traversed in order and * shapes are reordered accordingly. * <br/> * Every single existing shape must be referenced in that list; invalid/non-existing ids are ignored. * <br/> * The new ordering is only applied once all ids have been checked. This implies that, * if an exception is thrown then the original ordering is left intact. * * @param idsList list of shape ids (must at least contain all ids of currently contained shapes, additional ids are ignored) * @throws IllegalArgumentException if the reordered list of shapes would not contain all current shapes anymore */ public void reorderListOfAllShapes(List<String> idsList){ if (idsList == null) throw new IllegalArgumentException("list of ids is null"); else if (idsList.size() < shapes.size()) throw new IllegalArgumentException( "Ordered list of ids does not have enough members! Required: " + shapes.size() + ", Given: " + idsList.size()); List<S> newList = new ArrayList<S>(shapes.size()); //go through given id list in order for (String id: idsList){ if (id == null || "".equals(id.trim())) continue; //find the corresponding shape for (S shape: shapes){ //and add it to the new list (if not already in it) if (id.equals(shape.getResourceId()) && !newList.contains(shape)){ newList.add(shape); break; } } } //check if new list is valid if (newList.size() != shapes.size()) throw new IllegalArgumentException("Number of elements in new list differs from number of actual shapes! New: " + newList.size() + ", Old: " + shapes.size()); //apply new list shapes = newList; } /** * Returns the shape with the given resource id (may return the diagram itself if the id fits). * <p> * Returns null if the shape is not found. * <p> * Returns null if the input is null or "". * * @param id * @return */ public S getShapeById(String id) { if (id == null || "".equals(id)) return null; if (id.equals(getResourceId())) return (S) this; for (S shape : this.shapes) { if (id.equals(shape.getResourceId())) return shape; } return null; } /** * Returns the shapes with the given resource ids * * @param ids * @return a list of shapes that have one of the given ids */ public List<S> getShapesByIds(List<String> ids) { List<S> shapes = new ArrayList<S>(); if (ids == null) return shapes; for (String id : ids) { S shape = this.getShapeById(id); if (shape != null) shapes.add(shape); } return shapes; } /** * Returns all shapes that include the given point (in absolute coordinates). * * @param point * @return */ public List<S> getShapesAtPosition(Point point) { List<S> shapes = new ArrayList<S>(); if (point == null) return shapes; for (S shape : this.getAllShapesReadOnly()) { if (shape.getAbsoluteBounds().isPointIncluded(point)) shapes.add(shape); } return shapes; } /** * Always returns null! */ @Override public S getParent() { return null; } /** * Can't set a parent for a diagram; method call is ignored! */ @Override public void setParent(S parent) { // do nothing LOGGER.warn("Tried to set parent for diagram '" + getResourceId() + "', was ignored!"); } /** * Return the string representation of the diagram. Uses the JSONBuilder. * * @return the JSON string representing this diagram or null (if an exception occurred) * @throws JSONException */ public String getString() throws JSONException { JSONObject json = getJSON(); if (json != null) return json.toString(); else return null; } /** * Return the JSON representation of the diagram. Uses the {@link GenericJSONBuilder}. * <p/> * Overwrite this method in subclasses if there is an extended version of the json builder. * * @return the JSON object representing this diagram or null (if an exception occurred) * @throws JSONException */ public JSONObject getJSON() throws JSONException { return GenericJSONBuilder.parseModel(this); } @Override public D getDiagram() { return (D) this; } @Override public void setDiagram(D diagram2) { //do nothing } }